/* 
 * This file is part of Utilities for .NET (U4N)
 * Copyright (C) 2007 FutureFog (lammothmrc@yahoo.it)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using Microsoft.Win32;

namespace WowAceUpdater.u4n
{
    /// <summary>
    /// Allows to save and restore location, size and other parameters of some components (Form, ListView, Splitter, ListViewSortManager, SplitContainer, ToolStrip, TabControl).
    /// </summary>
    [
        ToolboxBitmap(typeof(ResourcesBase), "LayoutSaver.bmp"),
        ProvideProperty("LayoutSaverKey", typeof(Component)),
        ProvideProperty("LayoutSaverTag", typeof(Component)),
        DefaultEvent("RestoreTagMismatch")
    ]
    public class LayoutSaver : Component, IExtenderProvider
    {
        private const string LAYOUTSAVER_DEFAULT_FILENAME = "LayoutSaver.xml";
        private string LAYOUTSAVER_FILENAME = LAYOUTSAVER_DEFAULT_FILENAME;

        #region Static members
        private static int _XmlAccessTimeout = 3000;
        private static LayoutSaverLocation? _DefaultLocation = null;

        /// <summary>
        /// Gets or sets the timeout (in milliseconds) to wait if "LayoutSaver.xml" is already used by another process or thread.
        /// </summary>
        /// <exception cref="ArgumentException">When the timeout is not greater then zero.</exception>
        [Browsable(false)]
        public static int XmlAccessTimeout
        {
            get { return _XmlAccessTimeout; }
            set
            {
                if(value <= 0)
                    throw new ArgumentException("The timeout must be greater then zero.");

                _XmlAccessTimeout = value;
            }
        }

        /// <summary>
        /// Gets or sets the default location where all instances of LayoutSaver will save and restore values.
        /// </summary>
        [Browsable(false)]
        public static LayoutSaverLocation? DefaultLocation
        {
            get { return _DefaultLocation; }
            set { _DefaultLocation = value; }
        }
        #endregion

        #region Class constructors
        private readonly Dictionary<Component, ComponentData> _Dictionary = new Dictionary<Component, ComponentData>();
        private Form _ParentForm = null;
        private ContainerControl _StripContainer = null;
        private FormLocationMode _FormLocationMode = FormLocationMode.IntoWorkingArea;
        private LayoutSaverLocation _LayoutSaverLocation = LayoutSaverLocation.FileSystem;
        private LayoutSaverParentKey _LayoutSaverParentKey = LayoutSaverParentKey.CurrentUser_Company_Title;
        private LayoutSaverRootFolder _LayoutSaverRootFolder = LayoutSaverRootFolder.CurrentUser_Company_Title;
        private Assembly _LocationAssembly = null;
        private readonly XmlDocument _XmlDocument = new XmlDocument();

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        public LayoutSaver()
        { }

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="layoutSaverParentKey">The registry location where LayoutSaver will saves its key and subkeys.</param>
        public LayoutSaver(LayoutSaverParentKey layoutSaverParentKey)
        {
            _LayoutSaverLocation = LayoutSaverLocation.Registry;
            _LayoutSaverParentKey = layoutSaverParentKey;
        }

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="layoutSaverParentKey">The registry location where LayoutSaver will saves its key and subkeys.</param>
        /// <param name="parentForm">The Form exposing the events FormClosing and Load, used to save and restore values.</param>
        public LayoutSaver(LayoutSaverParentKey layoutSaverParentKey, Form parentForm)
        {
            _LayoutSaverLocation = LayoutSaverLocation.Registry;
            _LayoutSaverParentKey = layoutSaverParentKey;
            ParentForm = parentForm;
        }

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="layoutSaverRootFolder">The folder where LayoutSaver will create the file "LayoutSaver.xml".</param>
        public LayoutSaver(LayoutSaverRootFolder layoutSaverRootFolder)
        {
            _LayoutSaverLocation = LayoutSaverLocation.FileSystem;
            _LayoutSaverRootFolder = layoutSaverRootFolder;
        }

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="layoutSaverRootFolder">The folder where LayoutSaver will create the file "LayoutSaver.xml".</param>
        /// <param name="parentForm">The Form exposing the events FormClosing and Load, used to save and restore values.</param>
        public LayoutSaver(LayoutSaverRootFolder layoutSaverRootFolder, Form parentForm)
        {
            _LayoutSaverLocation = LayoutSaverLocation.FileSystem;
            _LayoutSaverRootFolder = layoutSaverRootFolder;
            ParentForm = parentForm;
        }
        #endregion

        #region Exposed events
        /// <summary>
        /// Occurs when the layout of a component is about to be restored and the tag associated to the control differs from the saved one.
        /// </summary>
        public event RestoreTagMismatchEventHandler RestoreTagMismatch = null;

        /// <summary>
        /// Occurs when the layout of a component is about to be saved.
        /// </summary>
        public event BeforeLayoutActionEventHandler BeforeLayoutSaving = null;

        /// <summary>
        /// Occurs when the layout of a component is about to be restored.
        /// </summary>
        public event BeforeLayoutActionEventHandler BeforeLayoutRestoring = null;

        /// <summary>
        /// Occurs when the layout of a component has been saved.
        /// </summary>
        public event AfterLayoutActionEventHandler AfterLayoutSaving = null;

        /// <summary>
        /// Occurs when the layout of a component has been restored.
        /// </summary>
        public event AfterLayoutActionEventHandler AfterLayoutRestoring = null;
        #endregion

        #region Implementation of IExtenderProvider
        /// <summary>
        /// Returns a value indicating if an object is a component that can be extended in the designer.
        /// </summary>
        /// <param name="component">The object to check.</param>
        /// <returns>A value indicating if an object is a component that can be extended in the designer.</returns>
        public bool CanExtend(object component)
        {
            return
                (component is Form) ||
                (component is ListView) ||
                (component is Splitter) ||
                (component is ListViewSortManager) ||
                (component is SplitContainer) ||
                (component is ToolStrip) ||
                (component is TabControl);
        }
        #endregion

        #region Public methods
        /// <summary>
        /// Associates to a component the key where its values will be saved to.
        /// </summary>
        /// <param name="component">The component to customize.</param>
        /// <param name="key">The key where the component's values will be saved to. If the key is not specified, no value will be saved; if the key is "\" a default value will be created.</param>
        [
            Category("LayoutSaver"),
            Description("The key where Control's values will be saved to. If the key is not specified, no value will be saved; if the key is \"\\\" a default value will be created."),
            DefaultValue("")
        ]
        public void SetLayoutSaverKey(Component component, string key)
        {
            if(CanExtend(component))
            {
                switch(key)
                {
                    case null:
                        break;

                    case "\\":
                        key = CreateParentKeyName(component).Replace("\\", "/");
                        break;

                    default:
                        key = key.Replace("\\", "/");
                        break;
                }

                if(_Dictionary.ContainsKey(component))
                    _Dictionary[component].Key = key;
                else
                    _Dictionary.Add(component, new ComponentData(key, string.Empty));
            }
        }

        /// <summary>
        /// Returns a value indicating the key where the component's value will be saved to.
        /// </summary>
        /// <param name="component">The component to query.</param>
        /// <returns>The key where the component's values will be saved to, or empty if the component's values will not be saved.</returns>
        [
            Category("LayoutSaver"),
            Description("The key where Control's values will be saved to. If the key is not specified, no value will be saved; if the key is \"\\\" a default value will be created."),
            DefaultValue("")
        ]
        public string GetLayoutSaverKey(Component component)
        {
            if(_Dictionary.ContainsKey(component))
                return _Dictionary[component].Key;

            return string.Empty;
        }

        /// <summary>
        /// Associates to a component an identifier (for example a version) for the saved settings.
        /// </summary>
        /// <param name="component">The component to query.</param>
        /// <param name="tag">The identifier (for example a version) for the saved settings.</param>
        [
            Category("LayoutSaver"),
            Description("The identifier (for example a version) for the saved settings."),
            DefaultValue("")
        ]
        public void SetLayoutSaverTag(Component component, string tag)
        {
            if(CanExtend(component))
            {
                if(_Dictionary.ContainsKey(component))
                    _Dictionary[component].Tag = tag;
                else
                    _Dictionary.Add(component, new ComponentData(string.Empty, tag));
            }
        }

        /// <summary>
        /// Returns the identifier (for example a version) for the saved settings.
        /// </summary>
        /// <param name="component">The component to query.</param>
        /// <returns>The identifier (for example a version) for the saved settings.</returns>
        [
            Category("LayoutSaver"),
            Description("The identifier (for example a version) for the saved settings."),
            DefaultValue("")
        ]
        public string GetLayoutSaverTag(Component component)
        {
            if(_Dictionary.ContainsKey(component))
                return _Dictionary[component].Tag;

            return string.Empty;
        }

        /// <summary>
        /// Removes a component from the LayoutSaver's list.
        /// </summary>
        /// <param name="component">The component to remove from the LayoutSaver's list.</param>
        public void RemoveComponent(Component component)
        {
            if(_Dictionary.ContainsKey(component))
                _Dictionary.Remove(component);
        }

        /// <summary>
        /// Saves the layout of a component contained in the LayoutSaver's list.
        /// </summary>
        /// <param name="component">The component whose layout will be saved.</param>
        /// <returns>True or false for success or failure.</returns>
        public bool SaveComponentLayout(Component component)
        {
            return SaveComponentLayout(component, true);
        }

        /// <summary>
        /// Restores the layout of a component contained in the LayoutSaver's list.
        /// </summary>
        /// <param name="component">The component whose layout will be restored.</param>
        /// <returns>True or false for success or failure.</returns>
        public bool RestoreComponentLayout(Component component)
        {
            return RestoreComponentLayout(component, true);
        }

        /// <summary>
        /// Saves the layout of all components contained in the LayoutSaver's list.
        /// </summary>
        /// <returns>True or false for success or failure.</returns>
        public bool SaveAllComponentsLayout()
        {
            bool success = true;

            if(GetCurrentLocation() == LayoutSaverLocation.FileSystem)
                CreateEmptyXml();

            foreach(Component component in _Dictionary.Keys)
            {
                if(_Dictionary[component].Key != string.Empty)
                    success &= SaveComponentLayout(component, false);
            }

            if(GetCurrentLocation() == LayoutSaverLocation.FileSystem)
            {
                success &= SaveXmlDocument(GetCurrentRootFolder(true, true));
                CreateEmptyXml();
            }

            return success;
        }

        /// <summary>
        /// Restores the layout of all components contained in the LayoutSaver's list.
        /// </summary>
        /// <returns>True or false for success or failure.</returns>
        public bool RestoreAllComponentsLayout()
        {
            bool success = true;

            if((GetCurrentLocation() != LayoutSaverLocation.FileSystem) || LoadXmlDocument(GetCurrentRootFolder(false, true)))
            {
                foreach(Component component in GetRestoreOrderList())
                    success &= RestoreComponentLayout(component, false);
            }
            else
            {
                success = false;
            }

            if(GetCurrentLocation() == LayoutSaverLocation.FileSystem)
                CreateEmptyXml();

            return success;
        }

        /// <summary>
        /// Deletes all values saved in the registry for the specified key.
        /// </summary>
        /// <returns>True or false for success or failure.</returns>
        public bool DeleteAllSavedValues()
        {
            switch(GetCurrentLocation())
            {
                case LayoutSaverLocation.Registry:
                    RegistryKey regKey = GetCurrentParentKey(GetCurrentParentKeyMode.Delete, null);
                    if(regKey == null)
                        return false;

                    try
                    {
                        regKey.DeleteSubKeyTree("LayoutSaver");
                        return true;
                    }
                    catch
                    {
                        return false;
                    }
                    finally
                    {
                        regKey.Close();
                    }

                case LayoutSaverLocation.FileSystem:
                    string xmlFile = GetCurrentRootFolder(false, true);
                    if(xmlFile == null)
                        return false;

                    try
                    {
                        File.Delete(xmlFile);
                        return true;
                    }
                    catch
                    {
                        return false;
                    }

                default:
                    return false;
            }
        }

        /// <summary>
        /// Returns the RegistryKey where LayoutSaver will save its key and subkeys.
        /// </summary>
        /// <param name="create">Indicates whether to create the key if it does not exist.</param>
        /// <returns>The RegistryKey or null if an error has occurred.</returns>
        public RegistryKey GetRegistryParentKey(bool create)
        {
            return GetCurrentParentKey(create ? GetCurrentParentKeyMode.Create : GetCurrentParentKeyMode.Delete, null);
        }

        /// <summary>
        /// Returns the path of the folder where LayoutSaver will create the file "LayoutSaver.xml".
        /// </summary>
        /// <param name="create">Indicates whether to create the folder if it does not exist.</param>
        /// <returns>The the path of the folder or null if an error has occurred.</returns>
        public string GetPathOfRootFolder(bool create)
        {
            return GetCurrentRootFolder(create, false);
        }
        #endregion

        #region Public properties
        /// <summary>
        /// Gets or sets the Form exposing the events FormClosing and Load, used to save and restore values.
        /// </summary>
        [
            Category("Form"),
            Description("The Form exposing the events FormClosing and Load, used to save and restore values."),
            DefaultValue(null)
        ]
        public Form ParentForm
        {
            get { return _ParentForm; }
            set
            {
                if(_ParentForm != null)
                {
                    _ParentForm.FormClosing -= OnFormClosing;
                    _ParentForm.Load -= OnFormLoad;
                }

                _ParentForm = value;

                if(_ParentForm != null)
                {
                    _ParentForm.FormClosing += OnFormClosing;
                    _ParentForm.Load += OnFormLoad;

                    if(_StripContainer == null)
                        _StripContainer = _ParentForm;
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the mode of restoring Form's location into the screen.
        /// </summary>
        [
            Category("Form"),
            Description("A value indicating the mode of restoring Form's location into the screen."),
            DefaultValue(FormLocationMode.IntoWorkingArea)
        ]
        public FormLocationMode FormLocationMode
        {
            get { return _FormLocationMode; }
            set { _FormLocationMode = value; }
        }

        /// <summary>
        /// Gets or sets the ContainerControl that contains all the ToolStripItems and ToolStripPanels.
        /// </summary>
        [
            Category("ToolStrips"),
            Description("The ContainerControl that contains all the ToolStripItems and ToolStripPanels."),
            DefaultValue(null)
        ]
        public ContainerControl StripContainer
        {
            get { return _StripContainer; }
            set { _StripContainer = value; }
        }

        /// <summary>
        /// Gets or sets the location where LayoutSaver will save and restore values.
        /// </summary>
        [
            Category("Location"),
            Description("The location where LayoutSaver will save and restore values."),
            DefaultValue(LayoutSaverLocation.FileSystem)
        ]
        public LayoutSaverLocation LayoutSaverLocation
        {
            get { return _LayoutSaverLocation; }
            set { _LayoutSaverLocation = value; }
        }

        /// <summary>
        /// Gets or sets the registry location where LayoutSaver will saves its key and subkeys.
        /// </summary>
        [
            Category("Location"),
            Description("The registry location where LayoutSaver will saves its key and subkeys."),
            DefaultValue(LayoutSaverParentKey.CurrentUser_Company_Title)
        ]
        public LayoutSaverParentKey LayoutSaverParentKey
        {
            get { return _LayoutSaverParentKey; }
            set { _LayoutSaverParentKey = value; }
        }

        /// <summary>
        /// Gets or sets the folder where LayoutSaver will create the file "LayoutSaver.xml".
        /// </summary>
        [
            Category("Location"),
            Description("The folder where LayoutSaver will create the file \"LayoutSaver.xml\"."),
            DefaultValue(LayoutSaverRootFolder.CurrentUser_Company_Title)
        ]
        public LayoutSaverRootFolder LayoutSaverRootFolder
        {
            get { return _LayoutSaverRootFolder; }
            set { _LayoutSaverRootFolder = value; }
        }

        /// <summary>
        /// Gets or sets the assembly that gives the name to the LayoutSaver registry key or root folder.
        /// </summary>
        [
            Browsable(false),
            DefaultValue(null)
        ]
        public Assembly LocationAssembly
        {
            get { return _LocationAssembly; }
            set { _LocationAssembly = value; }
        }

        [
            Category("Location"),
            Description("The name of the save file to create (Default \"LayoutSaver.xml\")."),
            DefaultValue(LAYOUTSAVER_DEFAULT_FILENAME)
        ]
        public string LayoutFileName
        {
            get { return LAYOUTSAVER_FILENAME; }
            set { LAYOUTSAVER_FILENAME = value; }
        }

        #endregion

        #region Events handling
        /// <summary>
        /// Event fired when the Form has been loaded, and all values must be restored.
        /// </summary>
        /// <param name="sender">The Form that has fired the event.</param>
        /// <param name="eventArgs">Event's arguments.</param>
        private void OnFormLoad(object sender, EventArgs eventArgs)
        {
            RestoreAllComponentsLayout();
        }

        /// <summary>
        /// Event fired when the Form is about to close, and all values must be saved.
        /// </summary>
        /// <param name="sender">The Form that has fired the event.</param>
        /// <param name="formClosingEventArgs">Event's arguments.</param>
        private void OnFormClosing(object sender, FormClosingEventArgs formClosingEventArgs)
        {
            SaveAllComponentsLayout();
        }
        #endregion

        #region Private methods
        /// <summary>
        /// Specify the mode for getting the registry parent key.
        /// </summary>
        private enum GetCurrentParentKeyMode
        {
            /// <summary>
            /// Opens or creates the key with privileges for writing.
            /// </summary>
            Save,

            /// <summary>
            /// Open the key for reading values.
            /// </summary>
            Restore,

            /// <summary>
            /// Opens the parent key containing all subkeys and values to delete.
            /// </summary>
            Delete,

            /// <summary>
            /// Creates the parent key containing all subkeys and values to delete.
            /// </summary>
            Create
        }

        /// <summary>
        /// Returns the key where values will be saved and restored or the key containing the subkey to delete.
        /// </summary>
        /// <param name="getCurrentParentKeyMode">Specify the mode for getting the parent key.</param>
        /// <param name="componentKey">Specify the name of the key associated to the component (for saving or restoring only).</param>
        /// <returns>The requested key or null if an error has occurred.</returns>
        private RegistryKey GetCurrentParentKey(GetCurrentParentKeyMode getCurrentParentKeyMode, string componentKey)
        {
            try
            {
                Assembly assembly = _LocationAssembly ?? Assembly.GetEntryAssembly();
                string company = (assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false)[0] as AssemblyCompanyAttribute).Company.Replace("\\", "/");
                string title = (assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false)[0] as AssemblyTitleAttribute).Title.Replace("\\", "/");

                if(company == string.Empty)
                    company = null;

                if(title == string.Empty)
                    title = null;

                if(componentKey == string.Empty)
                    componentKey = null;

                if((componentKey == null) && ((getCurrentParentKeyMode == GetCurrentParentKeyMode.Save) || (getCurrentParentKeyMode == GetCurrentParentKeyMode.Restore)))
                    return null;

                RegistryKey regKey;
                string regPath;

                switch(_LayoutSaverParentKey)
                {
                    case LayoutSaverParentKey.CurrentUser_Company_Title:
                        if((company == null) || (title == null))
                            return null;

                        regKey = Registry.CurrentUser;
                        regPath = string.Format("{0}\\{1}", company, title);
                        break;

                    case LayoutSaverParentKey.CurrentUser_Title:
                        if(title == null)
                            return null;

                        regKey = Registry.CurrentUser;
                        regPath = title;
                        break;

                    case LayoutSaverParentKey.LocalMachine_Company_Title:
                        if((company == null) || (title == null))
                            return null;

                        regKey = Registry.LocalMachine;
                        regPath = string.Format("{0}\\{1}", company, title);
                        break;

                    case LayoutSaverParentKey.LocalMachine_Title:
                        if(title == null)
                            return null;

                        regKey = Registry.LocalMachine;
                        regPath = title;
                        break;

                    default:
                        return null;
                }

                switch(getCurrentParentKeyMode)
                {
                    case GetCurrentParentKeyMode.Save:
                        regPath = string.Format("Software\\{0}\\LayoutSaver\\{1}", regPath, componentKey);
                        return regKey.CreateSubKey(regPath);

                    case GetCurrentParentKeyMode.Restore:
                        regPath = string.Format("Software\\{0}\\LayoutSaver\\{1}", regPath, componentKey);
                        return regKey.OpenSubKey(regPath, false);

                    case GetCurrentParentKeyMode.Delete:
                        regPath = string.Concat("Software\\", regPath);
                        return regKey.OpenSubKey(regPath, true);

                    case GetCurrentParentKeyMode.Create:
                        regPath = string.Concat("Software\\", regPath);
                        return regKey.CreateSubKey(regPath);

                    default:
                        return null;
                }
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Returns the folder where values will be saved and restored using the file "LayoutSaver.xml".
        /// </summary>
        /// <param name="create">Indicates whether to create the folder if it does not exist.</param>
        /// <param name="withXml">Indicates whether to append "LayoutSaver.xml" at the end of the path.</param>
        /// <returns>The requested folder or null if an error has occurred.</returns>
        private string GetCurrentRootFolder(bool create, bool withXml)
        {
            try
            {
                Assembly assembly = _LocationAssembly ?? Assembly.GetEntryAssembly();
                string company = MiscTools.RemoveInvalidPathChars((assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false)[0] as AssemblyCompanyAttribute).Company, true);
                string title = MiscTools.RemoveInvalidPathChars((assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false)[0] as AssemblyTitleAttribute).Title, true);
                
                
                AssemblyInformationalVersionAttribute infoversion =
                    (assembly.GetCustomAttributes(
                         typeof (AssemblyInformationalVersionAttribute), false)[0] as
                     AssemblyInformationalVersionAttribute);
                
                string version;
                if (infoversion == null)
                    version =
                        Assembly.GetExecutingAssembly().GetName().Version.ToString(4);
                else
                    version = infoversion.InformationalVersion;

                if(company == string.Empty)
                    company = null;

                if(title == string.Empty)
                    title = null;

                Environment.SpecialFolder? rootPath;
                string fullPath;

                switch(_LayoutSaverRootFolder)
                {
                    case LayoutSaverRootFolder.CurrentUser_Company_Title_Version:
                        if ((company == null) || (title == null) || version == null)
                            return null;

                        rootPath = Environment.SpecialFolder.LocalApplicationData;
                        fullPath = string.Format("{0}{1}{2}{3}{4}",
                                                 company, Path.DirectorySeparatorChar,
                                                 title, Path.DirectorySeparatorChar,
                                                 version);
                        break;

                    case LayoutSaverRootFolder.CurrentUser_Company_Title:
                        if((company == null) || (title == null))
                            return null;

                        rootPath = Environment.SpecialFolder.LocalApplicationData;
                        fullPath = string.Format("{0}{1}{2}", company, Path.DirectorySeparatorChar, title);
                        break;

                    case LayoutSaverRootFolder.CurrentUser_Title:
                        if(title == null)
                            return null;

                        rootPath = Environment.SpecialFolder.LocalApplicationData;
                        fullPath = title;
                        break;

                    case LayoutSaverRootFolder.LocalMachine_Company_Title:
                        if((company == null) || (title == null))
                            return null;

                        rootPath = Environment.SpecialFolder.CommonApplicationData;
                        fullPath = string.Format("{0}{1}{2}", company, Path.DirectorySeparatorChar, title);
                        break;

                    case LayoutSaverRootFolder.LocalMachine_Company_Title_Version:
                        if ((company == null) || (title == null) || version == null)
                            return null;

                        rootPath = Environment.SpecialFolder.CommonApplicationData;
                        fullPath = string.Format("{0}{1}{2}{3}{4}", 
                                                 company, Path.DirectorySeparatorChar, 
                                                 title, Path.DirectorySeparatorChar, 
                                                 version);
                        break;

                    case LayoutSaverRootFolder.LocalMachine_Title:
                        if(title == null)
                            return null;

                        rootPath = Environment.SpecialFolder.CommonApplicationData;
                        fullPath = title;
                        break;

                    case LayoutSaverRootFolder.Assembly_Folder:
                        rootPath = null;
                        fullPath = MiscTools.GetAssemblyFolder(assembly, true);
                        break;

                    default:
                        return null;
                }

                if(rootPath.HasValue)
                    fullPath = MiscTools.AppendFolderSeparator(Path.Combine(Environment.GetFolderPath(rootPath.Value), fullPath));

                if(create && (!Directory.Exists(fullPath)))
                    Directory.CreateDirectory(fullPath);

                return withXml ? Path.Combine(fullPath, LayoutFileName) : fullPath;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Returns the current used location where LayoutSaver will save and restore values.
        /// </summary>
        /// <returns>The current used location where LayoutSaver will save and restore values.</returns>
        private LayoutSaverLocation GetCurrentLocation()
        {
            return _DefaultLocation.HasValue ? _DefaultLocation.Value : _LayoutSaverLocation;
        }

        /// <summary>
        /// Creates a default key name for a component.
        /// </summary>
        /// <param name="component">The component to create the key name for.</param>
        /// <returns>The key name created for the component.</returns>
        private string CreateParentKeyName(Component component)
        {
            try
            {
                if(component is Control)
                {
                    if(component is Form)
                    {
                        if(!string.IsNullOrEmpty((component as Form).Name))
                            return (component as Form).Name;
                    }
                    else
                    {
                        Form form = (component as Control).FindForm();
                        if((form != null) && (!string.IsNullOrEmpty(form.Name)) && (!string.IsNullOrEmpty((component as Control).Name)))
                            return string.Format("{0}.{1}", form.Name, (component as Control).Name);
                    }
                }
                else if((_ParentForm != null) && (!string.IsNullOrEmpty(_ParentForm.Name)) && (component.Site != null) && (!string.IsNullOrEmpty(component.Site.Name)))
                {
                    return string.Format("{0}.{1}", _ParentForm.Name, component.Site.Name);
                }
            }
            catch
            { }
            return Guid.NewGuid().ToString();
        }

        /// <summary>
        /// Returns the right order for restoring components' layout.
        /// </summary>
        /// <returns>The right order for restoring components' layout.</returns>
        private List<Component> GetRestoreOrderList()
        {
            try
            {
                SortedDictionary<int, List<Component>> parents = new SortedDictionary<int, List<Component>>();

                foreach(Component component in _Dictionary.Keys)
                {
                    if(_Dictionary[component].Key != string.Empty)
                    {
                        int count = 0;

                        if(component is Control)
                        {
                            Control child = (Control)component;
                            Control parent = (Control)child.GetContainerControl();

                            while(child != parent)
                            {
                                child = parent;
                                parent = (Control)child.GetContainerControl();
                                count++;
                            }
                        }

                        if(!parents.ContainsKey(count))
                            parents.Add(count, new List<Component>());

                        parents[count].Add(component);
                    }
                }

                List<Component> components = new List<Component>();
                foreach(List<Component> list in parents.Values)
                    components.AddRange(list);
                return components;
            }
            catch
            {
                List<Component> components = new List<Component>();
                foreach(Component component in _Dictionary.Keys)
                {
                    if(_Dictionary[component].Key != string.Empty)
                        components.Add(component);
                }
                return components;
            }
        }

        /// <summary>
        /// Searches for a ToolStripItem with a given name.
        /// </summary>
        /// <param name="name">The name of the ToolStripItem to search.</param>
        /// <param name="parent">The container control where to start searching from.</param>
        /// <returns>The ToolStripItem or null if none is found.</returns>
        private ToolStripItem GetToolStripItemByName(string name, Control parent)
        {
            if(string.IsNullOrEmpty(name) || (parent == null))
                return null;

            foreach(Control child in parent.Controls)
            {
                if(child is ToolStrip)
                {
                    if((child as ToolStrip).Items.ContainsKey(name))
                        return (child as ToolStrip).Items[name];
                }

                ToolStripItem item = GetToolStripItemByName(name, child);
                if(item != null)
                    return item;
            }

            return null;
        }

        /// <summary>
        /// Searches for a ToolStripPanel with a given name.
        /// </summary>
        /// <param name="name">The name of the ToolStripPanel to search.</param>
        /// <param name="position">The position of the ToolStripPanel if it is part of a ToolStripContainer.</param>
        /// <param name="parent">The container control where to start searching from.</param>
        /// <returns>The ToolStripPanel or null if none is found.</returns>
        private ToolStripPanel GetToolStripPanelByName(string name, AnchorStyles position, Control parent)
        {
            if(string.IsNullOrEmpty(name) || (parent == null))
                return null;

            foreach(Control child in parent.Controls)
            {
                if(child.Name == name)
                {
                    if((position == AnchorStyles.None) && (child is ToolStripPanel))
                        return (ToolStripPanel)child;

                    if(child is ToolStripContainer)
                    {
                        switch(position)
                        {
                            case AnchorStyles.Left:
                                return (child as ToolStripContainer).LeftToolStripPanel;

                            case AnchorStyles.Top:
                                return (child as ToolStripContainer).TopToolStripPanel;

                            case AnchorStyles.Right:
                                return (child as ToolStripContainer).RightToolStripPanel;

                            case AnchorStyles.Bottom:
                                return (child as ToolStripContainer).BottomToolStripPanel;
                        }
                    }
                }

                ToolStripPanel panel = GetToolStripPanelByName(name, position, child);
                if(panel != null)
                    return panel;
            }

            return null;
        }
        #endregion

        #region Load and save values
        /// <summary>
        /// Saves the layout of a component contained in the LayoutSaver's list.
        /// </summary>
        /// <param name="component">The component whose layout will be saved.</param>
        /// <param name="onlyThis">Indicates if there is only this component.</param>
        /// <returns>True or false for success or failure.</returns>
        private bool SaveComponentLayout(Component component, bool onlyThis)
        {
            if((component == null) || (!_Dictionary.ContainsKey(component)))
                return false;

            if(this.BeforeLayoutSaving != null)
            {
                BeforeLayoutActionEventArgs args = new BeforeLayoutActionEventArgs(component);
                this.BeforeLayoutSaving(this, args);
                if(args.AbortAction)
                    return true;
            }

            object location = null;

            try
            {
                switch(GetCurrentLocation())
                {
                    case LayoutSaverLocation.Registry:
                        location = GetCurrentParentKey(GetCurrentParentKeyMode.Save, _Dictionary[component].Key);
                        break;

                    case LayoutSaverLocation.FileSystem:
                        if(onlyThis)
                            CreateEmptyXml();
                        location = FindXmlNode(_Dictionary[component].Key, true);
                        break;
                }

                if(location == null)
                    return false;

                SaveValue<string>(location, "Tag", GetLayoutSaverTag(component));

                if(component is Form)
                {
                    SaveValue<int>(location, "Maximized", ((component as Form).WindowState == FormWindowState.Maximized) ? 1 : 0);
                    Rectangle bounds = ((component as Form).WindowState == FormWindowState.Normal) ? (component as Form).DesktopBounds : (component as Form).RestoreBounds;
                    SaveValue<int>(location, "Left", bounds.Left);
                    SaveValue<int>(location, "Top", bounds.Top);
                    SaveValue<int>(location, "Width", bounds.Width);
                    SaveValue<int>(location, "Height", bounds.Height);
                }
                else if(component is ListView)
                {
                    for(int i = 0; i < (component as ListView).Columns.Count; i++)
                    {
                        SaveValue<int>(location, string.Format("Column_{0}_Width", i), (component as ListView).Columns[i].Width);
                        SaveValue<int>(location, string.Format("Column_{0}_DisplayIndex", i), (component as ListView).Columns[i].DisplayIndex);
                    }
                }
                else if(component is Splitter)
                {
                    if((component as Splitter).SplitPosition >= 0)
                        SaveValue<int>(location, "SplitPosition", (component as Splitter).SplitPosition);
                }
                else if(component is ListViewSortManager)
                {
                    SaveValue<int>(location, "SortIndex", (component as ListViewSortManager).SortIndex);
                    SaveValue<int>(location, "SortOrder", (int)(component as ListViewSortManager).SortOrder);
                }
                else if(component is SplitContainer)
                {
                    SaveValue<int>(location, "SplitterDistance", (component as SplitContainer).SplitterDistance);
                    SaveValue<bool>(location, "Panel1Collapsed", (component as SplitContainer).Panel1Collapsed);
                    SaveValue<bool>(location, "Panel2Collapsed", (component as SplitContainer).Panel2Collapsed);
                }
                else if(component is ToolStrip)
                {
                    SaveValue<int>(location, "Left", (component as ToolStrip).Left);
                    SaveValue<int>(location, "Top", (component as ToolStrip).Top);

                    string parent = string.Empty;
                    AnchorStyles position = AnchorStyles.None;

                    if((component as ToolStrip).Parent is ToolStripPanel)
                    {
                        if((component as ToolStrip).Parent.Parent is ToolStripContainer)
                        {
                            ToolStripPanel panel = (ToolStripPanel)(component as ToolStrip).Parent;
                            ToolStripContainer container = (ToolStripContainer)panel.Parent;
                            parent = container.Name;

                            if(panel.Equals(container.LeftToolStripPanel))
                            {
                                position = AnchorStyles.Left;
                            }
                            else if(panel.Equals(container.TopToolStripPanel))
                            {
                                position = AnchorStyles.Top;
                            }
                            else if(panel.Equals(container.RightToolStripPanel))
                            {
                                position = AnchorStyles.Right;
                            }
                            else if(panel.Equals(container.BottomToolStripPanel))
                            {
                                position = AnchorStyles.Bottom;
                            }
                        }
                        else
                        {
                            parent = (component as ToolStrip).Parent.Name;
                        }
                    }

                    SaveValue<string>(location, "ParentName", MiscTools.NullToEmpty(parent));
                    SaveValue<int>(location, "ParentPosition", (int)position);

                    List<string> items = new List<string>();
                    foreach(ToolStripItem item in (component as ToolStrip).Items)
                        items.Add(MiscTools.NullToEmpty(item.Name));

                    SaveValue<string[]>(location, "ToolStripItems", items.ToArray());
                }
                else if(component is TabControl)
                {
                    SaveValue<int>(location, "SelectedIndex", (component as TabControl).SelectedIndex);
                }

                if(onlyThis && (location is XmlNode) && (!SaveXmlDocument(GetCurrentRootFolder(true, true))))
                    return false;

                if(this.AfterLayoutSaving != null)
                    this.AfterLayoutSaving(this, new AfterLayoutActionEventArgs(component));

                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                try
                {
                    if(location is RegistryKey)
                        (location as RegistryKey).Close();
                }
                catch
                { }

                if(onlyThis && (location is XmlNode))
                    CreateEmptyXml();
            }
        }

        /// <summary>
        /// Restores the layout of a component contained in the LayoutSaver's list.
        /// </summary>
        /// <param name="component">The component whose layout will be restored.</param>
        /// <param name="onlyThis">Indicates if there is only this component.</param>
        /// <returns>True or false for success or failure.</returns>
        public bool RestoreComponentLayout(Component component, bool onlyThis)
        {
            if((component == null) || (!_Dictionary.ContainsKey(component)))
                return false;

            if(this.BeforeLayoutRestoring != null)
            {
                BeforeLayoutActionEventArgs args = new BeforeLayoutActionEventArgs(component);
                this.BeforeLayoutRestoring(this, args);
                if(args.AbortAction)
                    return true;
            }

            object location = null;

            try
            {
                switch(GetCurrentLocation())
                {
                    case LayoutSaverLocation.Registry:
                        location = GetCurrentParentKey(GetCurrentParentKeyMode.Restore, _Dictionary[component].Key);
                        break;

                    case LayoutSaverLocation.FileSystem:
                        if(onlyThis && (!LoadXmlDocument(GetCurrentRootFolder(false, true))))
                            return false;
                        location = FindXmlNode(_Dictionary[component].Key, false);
                        break;
                }

                if(location == null)
                    return false;

                RestoreTagMismatchEventArgs args = new RestoreTagMismatchEventArgs(
                    component, GetLayoutSaverTag(component), LoadValue<string>(location, "Tag", null));

                if(args.CurrentTag != args.SavedTag)
                {
                    if(this.RestoreTagMismatch != null)
                        this.RestoreTagMismatch(this, args);

                    if(args.AbortRestore)
                        return true;
                }

                if(component is Control)
                    (component as Control).CreateControl();

                if(component is Form)
                {
                    (component as Form).Left = LoadValue<int>(location, "Left", (component as Form).Left);
                    (component as Form).Top = LoadValue<int>(location, "Top", (component as Form).Top);

                    switch((component as Form).FormBorderStyle)
                    {
                        case FormBorderStyle.Sizable:
                        case FormBorderStyle.SizableToolWindow:
                            (component as Form).Width = LoadValue<int>(location, "Width", (component as Form).Width);
                            (component as Form).Height = LoadValue<int>(location, "Height", (component as Form).Height);
                            break;
                    }

                    Rectangle area = Rectangle.Empty;
                    switch(_FormLocationMode)
                    {
                        case FormLocationMode.IntoVirtualScreen:
                            area = SystemInformation.VirtualScreen;
                            break;

                        case FormLocationMode.IntoWorkingArea:
                            area = SystemInformation.WorkingArea;
                            break;
                    }

                    if(!area.Equals(Rectangle.Empty))
                    {
                        if((component as Form).Left < 0)
                            (component as Form).Left = area.Left;

                        if((component as Form).Top < 0)
                            (component as Form).Top = area.Top;

                        if((component as Form).Right > area.Right)
                        {
                            if((component as Form).Width >= area.Width)
                            {
                                (component as Form).Left = area.Left;
                                (component as Form).Width = area.Width;
                            }
                            else
                            {
                                (component as Form).Left = area.Right - (component as Form).Width;
                            }
                        }

                        if((component as Form).Bottom > area.Bottom)
                        {
                            if((component as Form).Height >= area.Height)
                            {
                                (component as Form).Top = area.Top;
                                (component as Form).Height = area.Height;
                            }
                            else
                            {
                                (component as Form).Top = area.Bottom - (component as Form).Height;
                            }
                        }
                    }

                    if(LoadValue<int>(location, "Maximized", 0) == 1)
                        (component as Form).WindowState = FormWindowState.Maximized;
                }
                else if(component is ListView)
                {
                    for(int i = 0; i < (component as ListView).Columns.Count; i++)
                    {
                        (component as ListView).Columns[i].Width = LoadValue<int>(location, string.Format("Column_{0}_Width", i), (component as ListView).Columns[i].Width);
                        (component as ListView).Columns[i].DisplayIndex = LoadValue<int>(location, string.Format("Column_{0}_DisplayIndex", i), (component as ListView).Columns[i].DisplayIndex);
                    }
                }
                else if(component is Splitter)
                {
                    (component as Splitter).SplitPosition = LoadValue<int>(location, "SplitPosition", (component as Splitter).SplitPosition);
                }
                else if(component is ListViewSortManager)
                {
                    int sortIndex = LoadValue<int>(location, "SortIndex", (component as ListViewSortManager).SortIndex);
                    SortOrder sortOrder = (SortOrder)LoadValue<int>(location, "SortOrder", (int)(component as ListViewSortManager).SortOrder);
                    (component as ListViewSortManager).Sort(sortIndex, sortOrder);
                }
                else if(component is SplitContainer)
                {
                    if(!(component as SplitContainer).IsSplitterFixed)
                        (component as SplitContainer).SplitterDistance = LoadValue<int>(location, "SplitterDistance", (component as SplitContainer).SplitterDistance);

                    (component as SplitContainer).Panel1Collapsed = LoadValue<bool>(location, "Panel1Collapsed", (component as SplitContainer).Panel1Collapsed);
                    (component as SplitContainer).Panel2Collapsed = LoadValue<bool>(location, "Panel2Collapsed", (component as SplitContainer).Panel2Collapsed);
                }
                else if(component is ToolStrip)
                {
                    if(_StripContainer != null)
                    {
                        if((component as ToolStrip).AllowItemReorder)
                        {
                            string[] items = LoadValue<string[]>(location, "ToolStripItems", null);
                            if(items != null)
                            {
                                for(int i = items.Length - 1; i >= 0; i--)
                                {
                                    ToolStripItem item = GetToolStripItemByName(items[i], _StripContainer);
                                    if(item != null)
                                        (component as ToolStrip).Items.Insert(0, item);
                                }
                            }
                        }

                        ToolStripPanel panel = GetToolStripPanelByName(LoadValue<string>(location, "ParentName", null), (AnchorStyles)LoadValue<int>(location, "ParentPosition", (int)AnchorStyles.None), _StripContainer);
                        if(panel != null)
                        {
                            int left = LoadValue<int>(location, "Left", (component as ToolStrip).Left);
                            int top = LoadValue<int>(location, "Top", (component as ToolStrip).Top);
                            panel.Join((ToolStrip)component, left, top);
                        }
                    }
                }
                else if(component is TabControl)
                {
                    (component as TabControl).SelectedIndex = LoadValue<int>(location, "SelectedIndex", (component as TabControl).SelectedIndex);
                }

                if(this.AfterLayoutRestoring != null)
                    this.AfterLayoutRestoring(this, new AfterLayoutActionEventArgs(component));

                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                try
                {
                    if(location is RegistryKey)
                        (location as RegistryKey).Close();
                }
                catch
                { }

                if(onlyThis && (location is XmlNode))
                    CreateEmptyXml();
            }
        }

        /// <summary>
        /// Saves a value into a registry key or into a node of the XML file.
        /// </summary>
        /// <typeparam name="T">The type of the value to save.</typeparam>
        /// <param name="location">The registry key or the node of the XML file.</param>
        /// <param name="name">The name of the value to save.</param>
        /// <param name="value">The content on the value to save.</param>
        /// <exception cref="Exception">A generic exception has occurred.</exception>
        private void SaveValue<T>(object location, string name, T value)
        {
            if(location is RegistryKey)
            {
                if(typeof(T) == typeof(bool))
                    (location as RegistryKey).SetValue(name, Convert.ToInt32(value));
                else
                    (location as RegistryKey).SetValue(name, value);
            }
            else if(location is XmlNode)
            {
                string temp;

                if(typeof(T) == typeof(string))
                {
                    temp = (string)(object)value;
                }
                else if(typeof(T) == typeof(string[]))
                {
                    temp = string.Join("\n", (string[])(object)value);
                }
                else if(typeof(T) == typeof(bool))
                {
                    temp = Convert.ToInt32(value).ToString();
                }
                else
                {
                    temp = value.ToString();
                }

                foreach(XmlAttribute attribute in (location as XmlNode).Attributes)
                {
                    if(attribute.Name == name)
                    {
                        attribute.Value = temp;
                        return;
                    }
                }

                (location as XmlNode).Attributes.Append(_XmlDocument.CreateAttribute(name)).Value = temp;
            }
        }

        /// <summary>
        /// Loads a value from a registry key or from a node of the XML file.
        /// </summary>
        /// <typeparam name="T">The type of the value to load.</typeparam>
        /// <param name="location">The registry key or the node of the XML file.</param>
        /// <param name="name">The name of the value to load.</param>
        /// <param name="replace">The value to return if loading fails.</param>
        /// <returns>The content of the loaded value.</returns>
        /// <exception cref="Exception">A generic exception has occurred.</exception>
        private static T LoadValue<T>(object location, string name, T replace)
        {
            if(location is RegistryKey)
            {
                if(typeof(T) == typeof(bool))
                    return (T)(object)Convert.ToBoolean((location as RegistryKey).GetValue(name, replace));
                else
                    return (T)(location as RegistryKey).GetValue(name, replace);
            }
            else if(location is XmlNode)
            {
                foreach(XmlAttribute attribute in (location as XmlNode).Attributes)
                {
                    if(attribute.Name == name)
                    {
                        if(typeof(T) == typeof(int))
                        {
                            return (T)(object)int.Parse(attribute.Value);
                        }
                        else if(typeof(T) == typeof(string))
                        {
                            return (T)(object)attribute.Value;
                        }
                        else if(typeof(T) == typeof(string[]))
                        {
                            return (T)(object)attribute.Value.Split('\n');
                        }
                        else if(typeof(T) == typeof(bool))
                        {
                            return (T)(object)Convert.ToBoolean(int.Parse(attribute.Value));
                        }
                    }
                }
            }

            return replace;
        }

        /// <summary>
        /// Searches, in the attributes of a node of the XML file, the key of a component.
        /// </summary>
        /// <param name="node">The node of the XML file to search in.</param>
        /// <returns>The key of the component or null if the node does not contain it.</returns>
        private static string GetComponentKey(XmlNode node)
        {
            if(node.Name == "Item")
                return LoadValue<string>(node, "Key", null);
            else
                return null;
        }
        #endregion

        #region XML file methods
        /// <summary>
        /// Returns the node containing the values of a specific component.
        /// </summary>
        /// <param name="componentKey">The key of the component or null to get the root.</param>
        /// <param name="createChild">Indicates whether to create the child node</param>
        /// <returns>The node of the XML file or null if it does not exist.</returns>
        private XmlNode FindXmlNode(string componentKey, bool createChild)
        {
            XmlNode root = _XmlDocument["LayoutSaver"];
            if(root == null)
            {
                CreateEmptyXml();
                root = _XmlDocument["LayoutSaver"];
            }

            if(componentKey == null)
                return root;

            foreach(XmlNode node in root.ChildNodes)
            {
                if(GetComponentKey(node) == componentKey)
                    return node;
            }

            if(!createChild)
                return null;

            XmlNode item = root.AppendChild(_XmlDocument.CreateElement("Item"));
            SaveValue<string>(item, "Key", componentKey);
            return item;
        }

        /// <summary>
        /// Creates the default empty XML structure.
        /// </summary>
        private void CreateEmptyXml()
        {
            try
            {
                _XmlDocument.LoadXml("<LayoutSaver />");
            }
            catch
            { }
        }

        /// <summary>
        /// Opens or create the mutex used to synchronize access to the XML file.
        /// </summary>
        /// <param name="xmlFile">The path to the XML file.</param>
        /// <returns>The mutex or null if an error has occurred.</returns>
        private static Mutex RetrieveXmlMutex(string xmlFile)
        {
            try
            {
                bool created;
                Mutex mutex = new Mutex(false, xmlFile.Replace('\\', '/'), out created);

                if(created)
                {
                    SecurityIdentifier everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);

                    MutexSecurity security = new MutexSecurity();
                    security.AddAccessRule(new MutexAccessRule(everyone, MutexRights.FullControl, AccessControlType.Allow));
                    mutex.SetAccessControl(security);
                }

                return mutex;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Releases the mutex used to synchronize access to the XML file.
        /// </summary>
        /// <param name="mutex">The mutex to release.</param>
        /// <param name="acquired">Indicates if the mutex has been acquired.</param>
        private static void ReleaseXmlMutex(Mutex mutex, bool acquired)
        {
            if(mutex != null)
            {
                if(acquired)
                {
                    try
                    {
                        mutex.ReleaseMutex();
                    }
                    catch
                    { }
                }

                mutex.Close();
            }
        }

        /// <summary>
        /// Loads the contents from the XML file.
        /// </summary>
        /// <param name="xmlFile">The path to the XML file.</param>
        /// <returns>True or false for success or failure.</returns>
        private bool LoadXmlDocument(string xmlFile)
        {
            Mutex mutex = RetrieveXmlMutex(xmlFile);
            bool acquired = false;

            try
            {
                if((mutex != null) && mutex.WaitOne(_XmlAccessTimeout, false))
                {
                    acquired = true;

                    if(File.Exists(xmlFile))
                        _XmlDocument.Load(xmlFile);
                    else
                        CreateEmptyXml();

                    return true;
                }

                CreateEmptyXml();
                return false;
            }
            catch
            {
                CreateEmptyXml();
                return false;
            }
            finally
            {
                ReleaseXmlMutex(mutex, acquired);
            }
        }

        /// <summary>
        /// Saves the contents into the XML file.
        /// </summary>
        /// <param name="xmlFile">The path to the XML file.</param>
        /// <returns>True or false for success or failure.</returns>
        private bool SaveXmlDocument(string xmlFile)
        {
            Mutex mutex = RetrieveXmlMutex(xmlFile);
            bool acquired = false;

            try
            {
                if((mutex != null) && mutex.WaitOne(_XmlAccessTimeout, false))
                {
                    acquired = true;

                    if(File.Exists(xmlFile))
                    {
                        Dictionary<string, XmlNode> nodes = new Dictionary<string, XmlNode>();
                        int count;

                        foreach(XmlNode node in FindXmlNode(null, false).ChildNodes)
                        {
                            string key = GetComponentKey(node);
                            if(key != null)
                                nodes.Add(key, node);
                        }

                        try
                        {
                            _XmlDocument.Load(xmlFile);
                            count = nodes.Count;
                        }
                        catch(XmlException)
                        {
                            CreateEmptyXml();
                            count = 0;
                        }

                        XmlNode root = FindXmlNode(null, false);

                        for(int i = root.ChildNodes.Count - 1; (i >= 0) && (count > 0); i--)
                        {
                            string key = GetComponentKey(root.ChildNodes[i]);
                            if((key != null) && nodes.ContainsKey(key))
                            {
                                root.RemoveChild(root.ChildNodes[i]);
                                count--;
                            }
                        }

                        foreach(KeyValuePair<string, XmlNode> pair in nodes)
                            root.AppendChild(pair.Value);
                    }

                    _XmlDocument.Save(xmlFile);
                    return true;
                }

                return false;
            }
            catch
            {
                return false;
            }
            finally
            {
                ReleaseXmlMutex(mutex, acquired);
            }
        }
        #endregion

        #region Class "ComponentData"
        /// <summary>
        /// Contains registry data for the components in the LayoutSaver's list.
        /// </summary>
        private class ComponentData
        {
            private string _Key;
            private string _Tag;

            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="key">The key where LayoutSaver will saves its values.</param>
            /// <param name="tag">The identifier (for example a version) for the saved settings.</param>
            public ComponentData(string key, string tag)
            {
                Key = key;
                Tag = tag;
            }

            /// <summary>
            /// Gets or sets the key where LayoutSaver will saves its values.
            /// </summary>
            public string Key
            {
                get { return _Key; }
                set { _Key = MiscTools.NullToEmpty(value); }
            }

            /// <summary>
            /// Gets or sets the identifier (for example a version) for the saved settings.
            /// </summary>
            public string Tag
            {
                get { return _Tag; }
                set { _Tag = MiscTools.NullToEmpty(value); }
            }
        }
        #endregion
    }

    /// <summary>
    /// Represents the location where LayoutSaver will save and restore values.
    /// </summary>
    public enum LayoutSaverLocation
    {
        /// <summary>
        /// Uses the registry to save and restore values.
        /// </summary>
        Registry,

        /// <summary>
        /// Uses the file-system to save and restore values.
        /// </summary>
        FileSystem
    }

    /// <summary>
    /// Represents the registry location where LayoutSaver will save its key and subkeys.
    /// </summary>
    public enum LayoutSaverParentKey
    {
        /// <summary>
        /// Represents the key: "HKEY_CURRENT_USER\Software\[AssemblyCompany]\[AssemblyTitle]".
        /// </summary>
        CurrentUser_Company_Title,

        /// <summary>
        /// Represents the key: "HKEY_CURRENT_USER\Software\[AssemblyTitle]".
        /// </summary>
        CurrentUser_Title,

        /// <summary>
        /// Represents the key: "HKEY_LOCAL_MACHINE\Software\[AssemblyCompany]\[AssemblyTitle]".
        /// </summary>
        LocalMachine_Company_Title,

        /// <summary>
        /// Represents the key: "HKEY_LOCAL_MACHINE\Software\[AssemblyTitle]".
        /// </summary>
        LocalMachine_Title
    }

    /// <summary>
    /// Represents the folder where LayoutSaver will create the file "LayoutSaver.xml".
    /// </summary>
    public enum LayoutSaverRootFolder
    {
        /// <summary>
        /// Represents the folder: "[Application data for the current user]\[AssemblyCompany]\[AssemblyTitle]".
        /// </summary>
        CurrentUser_Company_Title_Version,

        /// <summary>
        /// Represents the folder: "[Application data for the current user]\[AssemblyCompany]\[AssemblyTitle]".
        /// </summary>
        CurrentUser_Company_Title,

        /// <summary>
        /// Represents the folder: "[Application data for the current user]\[AssemblyTitle]".
        /// </summary>
        CurrentUser_Title,

        /// <summary>
        /// Represents the folder: "[Application data for all users]\[AssemblyCompany]\[AssemblyTitle]".
        /// </summary>
        LocalMachine_Company_Title_Version,

        /// <summary>
        /// Represents the folder: "[Application data for all users]\[AssemblyCompany]\[AssemblyTitle]".
        /// </summary>
        LocalMachine_Company_Title,

        /// <summary>
        /// Represents the folder: "[Application data for all users]\[AssemblyTitle]".
        /// </summary>
        LocalMachine_Title,

        /// <summary>
        /// Represents the folder where the EntryAssembly is located into.
        /// </summary>
        Assembly_Folder
    }

    /// <summary>
    /// Represents the modes of restoring Form's location into the screen.
    /// </summary>
    public enum FormLocationMode
    {
        /// <summary>
        /// Allows to place the Form also outside of the full screen area.
        /// </summary>
        AllowOutOfScreen,

        /// <summary>
        /// Places the Form inside the full screen area.
        /// </summary>
        IntoVirtualScreen,

        /// <summary>
        /// Places the Form inside the working area of the screen.
        /// </summary>
        IntoWorkingArea
    }

    /// <summary>
    /// Represents the method that will handle the RestoreTagMismatch event of a LayoutSaver.
    /// </summary>
    /// <param name="sender">The LayoutSaver that has raised the event.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void RestoreTagMismatchEventHandler(object sender, RestoreTagMismatchEventArgs e);

    /// <summary>
    /// Represents the method that will handle the BeforeLayoutSaving and BeforeLayoutRestoring events of a LayoutSaver.
    /// </summary>
    /// <param name="sender">The LayoutSaver that has raised the event.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void BeforeLayoutActionEventHandler(object sender, BeforeLayoutActionEventArgs e);

    /// <summary>
    /// Represents the method that will handle the AfterLayoutSaving and AfterLayoutRestoring events of a LayoutSaver.
    /// </summary>
    /// <param name="sender">The LayoutSaver that has raised the event.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void AfterLayoutActionEventHandler(object sender, AfterLayoutActionEventArgs e);

    /// <summary>
    /// Provides data for the RestoreTagMismatch event.
    /// </summary>
    public class RestoreTagMismatchEventArgs : EventArgs
    {
        private readonly Component _Component;
        private readonly string _CurrentTag;
        private readonly string _SavedTag;
        private bool _AbortRestore;

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="component">The component that is about to be restored.</param>
        /// <param name="currentTag">The tag now associated to the component.</param>
        /// <param name="savedTag">The tag saved in the registry.</param>
        public RestoreTagMismatchEventArgs(Component component, string currentTag, string savedTag)
        {
            _Component = component;
            _CurrentTag = currentTag;
            _SavedTag = savedTag;
            _AbortRestore = false;
        }

        /// <summary>
        /// Gets the component that is about to be restored.
        /// </summary>
        public Component Component
        {
            get { return _Component; }
        }

        /// <summary>
        /// Gets the tag now associated to the component.
        /// </summary>
        public string CurrentTag
        {
            get { return _CurrentTag; }
        }

        /// <summary>
        /// Gets the tag saved in the registry.
        /// </summary>
        public string SavedTag
        {
            get { return _SavedTag; }
        }

        /// <summary>
        /// Gets or sets a value indicating in the restore operation will be performed or aborted.
        /// </summary>
        public bool AbortRestore
        {
            get { return _AbortRestore; }
            set { _AbortRestore = value; }
        }
    }

    /// <summary>
    /// Provides data for the BeforeLayoutSaving and BeforeLayoutRestoring events.
    /// </summary>
    public class BeforeLayoutActionEventArgs : EventArgs
    {
        private readonly Component _Component;
        private bool _AbortAction;

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="component">The component on which the action will be performed.</param>
        public BeforeLayoutActionEventArgs(Component component)
        {
            _Component = component;
            _AbortAction = false;
        }

        /// <summary>
        /// Gets the component on which the action is about to be performed.
        /// </summary>
        public Component Component
        {
            get { return _Component; }
        }

        /// <summary>
        /// Gets or sets a value indicating in the action will be performed or aborted.
        /// </summary>
        public bool AbortAction
        {
            get { return _AbortAction; }
            set { _AbortAction = value; }
        }
    }

    /// <summary>
    /// Provides data for the AfterLayoutSaving and AfterLayoutRestoring events.
    /// </summary>
    public class AfterLayoutActionEventArgs : EventArgs
    {
        private readonly Component _Component;

        /// <summary>
        /// Creates a new instance of the class.
        /// </summary>
        /// <param name="component">The component on which the action has been performed.</param>
        public AfterLayoutActionEventArgs(Component component)
        {
            _Component = component;
        }

        /// <summary>
        /// Gets the component on which the action has been performed.
        /// </summary>
        public Component Component
        {
            get { return _Component; }
        }
    }
}